﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;
using System.Windows;
using System.Windows.Media;
using System.ComponentModel;

namespace Frontend.Grids
{
    class HighlightableGrid: BorderedGrid
    {
        static public readonly DependencyProperty HighlightBrushProperty;
        static public readonly DependencyProperty HighlightPenProperty;

        static readonly DependencyPropertyKey HighlightedRowPropertyKey;
        static public readonly DependencyProperty HighlightedRowProperty;
        static readonly DependencyPropertyKey HighlightedColumnPropertyKey;
        static public readonly DependencyProperty HighlightedColumnProperty;

        static public readonly DependencyProperty CurrentBrushProperty;
        static public readonly DependencyProperty CurrentPenProperty;

        static public readonly DependencyProperty CurrentRowProperty;
        static public readonly DependencyProperty CurrentColumnProperty;
        
        static HighlightableGrid()
        {
            FrameworkPropertyMetadata metadata;

            metadata = new FrameworkPropertyMetadata();
            metadata.AffectsRender = true;
            metadata.DefaultValue = null;

            HighlightBrushProperty = DependencyProperty.Register("HighlightBrush", 
                                     typeof(Brush), typeof(HighlightableGrid), 
                                     metadata);

            metadata = new FrameworkPropertyMetadata();
            metadata.AffectsRender = true;
            metadata.DefaultValue = null;

            HighlightPenProperty = DependencyProperty.Register("HighlightPen", 
                                   typeof(Pen), typeof(HighlightableGrid), 
                                   metadata);
                       
            metadata = new FrameworkPropertyMetadata();            
            metadata.AffectsRender = true;
            metadata.DefaultValue = null;

            HighlightedRowPropertyKey = DependencyProperty.RegisterReadOnly("HighlightedRow", 
                                        typeof(int?), typeof(HighlightableGrid), 
                                        metadata);
            HighlightedRowProperty = HighlightedRowPropertyKey.DependencyProperty;

            metadata = new FrameworkPropertyMetadata();
            metadata.AffectsRender = true;
            metadata.DefaultValue = null;

            HighlightedColumnPropertyKey = DependencyProperty.RegisterReadOnly("HighlightedColumn",
                                           typeof(int?), typeof(HighlightableGrid), 
                                           metadata);
            HighlightedColumnProperty = HighlightedColumnPropertyKey.DependencyProperty;


            metadata = new FrameworkPropertyMetadata();
            metadata.AffectsRender = true;
            metadata.DefaultValue = null;

            CurrentBrushProperty = DependencyProperty.Register("CurrentBrush",
                                   typeof(Brush), typeof(HighlightableGrid),
                                   metadata);

            metadata = new FrameworkPropertyMetadata();
            metadata.AffectsRender = true;
            metadata.DefaultValue = null;

            CurrentPenProperty = DependencyProperty.Register("CurrentPen",
                                 typeof(Pen), typeof(HighlightableGrid),
                                 metadata);

            metadata = new FrameworkPropertyMetadata();
            metadata.AffectsRender = true;
            metadata.DefaultValue = null;

            CurrentRowProperty = DependencyProperty.Register("CurrentRow",
                                                             typeof(int?), typeof(HighlightableGrid),
                                                             metadata);

            metadata = new FrameworkPropertyMetadata();
            metadata.AffectsRender = true;
            metadata.DefaultValue = null;

            CurrentColumnProperty = DependencyProperty.Register("CurrentColumn", 
                                                             typeof(int?), typeof(HighlightableGrid), 
                                                             metadata);
        }

        public HighlightableGrid()
        {
            this.MouseMove += OnMouseMove;

            DependencyPropertyDescriptor rowProp =
                DependencyPropertyDescriptor.FromProperty(HighlightedRowProperty, typeof(HighlightableGrid));

            rowProp.AddValueChanged(this, (s, e) => OnHighlightChanged());

            DependencyPropertyDescriptor columnProp =
                DependencyPropertyDescriptor.FromProperty(HighlightedColumnProperty, typeof(HighlightableGrid));

            columnProp.AddValueChanged(this, (s, e) => OnHighlightChanged());
        }

        public Brush HighlightBrush
        {
            get
            {
                return (Brush)GetValue(HighlightBrushProperty);
            }
            set
            {
                SetValue(HighlightBrushProperty, value);
            }
        }
        public Pen HighlightPen
        {
            get
            {
                return (Pen)GetValue(HighlightPenProperty);
            }
            set
            {
                SetValue(HighlightPenProperty, value);
            }
        }
       
        public int? HighlightedRow
        {
            get
            {
                return (int?)GetValue(HighlightedRowProperty);
            }
            private set
            {
                SetValue(HighlightedRowPropertyKey, value);
            }
        }
        public int? HighlightedColumn
        {
            get
            {
                return (int?)GetValue(HighlightedColumnProperty);
            }
            private set
            {
                SetValue(HighlightedColumnPropertyKey, value);
            }
        }

        public Brush CurrentBrush
        {
            get
            {
                return (Brush)GetValue(CurrentBrushProperty);
            }
            set
            {
                SetValue(CurrentBrushProperty, value);
            }
        }
        public Pen CurrentPen
        {
            get
            {
                return (Pen)GetValue(CurrentPenProperty);
            }
            set
            {
                SetValue(CurrentPenProperty, value);
            }
        }

        public int? CurrentRow
        {
            get
            {
                return (int?)GetValue(CurrentRowProperty);
            }
            set
            {
                SetValue(CurrentRowProperty, value);
            }
        }
        public int? CurrentColumn
        {
            get
            {
                return (int?)GetValue(CurrentColumnProperty);
            }
            set
            {
                SetValue(CurrentColumnProperty, value);
            }
        }

        Rect? GetRowRect(int rowIndex)
        {
            if (rowIndex >= RowDefinitions.Count) return null;

            double height = 0;
            int curRowIndex = 0;
            foreach (var r in this.RowDefinitions)
            {
                if (curRowIndex == rowIndex)
                {
                    return new Rect(0, height, RenderSize.Width, r.ActualHeight);                    
                }
                curRowIndex++;
                height += r.ActualHeight;
            }

            return null;
        }
        Rect? GetColumnRect(int columnIndex)
        {
            if (columnIndex >= ColumnDefinitions.Count) return null;

            double width = 0;
            int curColumnIndex = 0;
            foreach (var c in this.ColumnDefinitions)
            {
                if (curColumnIndex == columnIndex)
                {
                    return new Rect(width, 0, c.ActualWidth, RenderSize.Height);
                }
                curColumnIndex++;
                width += c.ActualWidth;
            }

            return null;
        }
        Rect? GetRect(int rowIndex, int columnIndex)
        {
            Rect? rowRect = GetRowRect(rowIndex);
            Rect? columnRect = GetColumnRect(columnIndex);
            Rect defaultRect = new Rect();

            if (rowRect == null || columnRect == null)
            {
                return null;
            }

            return Rect.Intersect(rowRect ?? defaultRect, columnRect ?? defaultRect);
        }

        int? GetMouseRow(double mouseY)
        {
            double height = 0;
            int row = 0;
            foreach (var r in this.RowDefinitions)
            {
                if (height < mouseY && mouseY < height + r.ActualHeight)
                {
                    return row;
                }
                height += r.ActualHeight;
                row++;
            }
            return null;
        }
        int? GetMouseColumn(double mouseX)
        {            
            double width = 0;
            int column = 0;
            foreach (var c in this.ColumnDefinitions)
            {
                if (width < mouseX && mouseX < width + c.ActualWidth)
                {
                    return column;
                }
                column++;
                width += c.ActualWidth;
            }

            return null;
        }
        
        protected override void OnRender(System.Windows.Media.DrawingContext dc)
        {
            Rect defaultRect = new Rect();

            if (CurrentColumn != null)
            {
                Rect? rect = GetColumnRect(CurrentColumn ?? 0);
                dc.DrawRectangle(CurrentBrush, CurrentPen, rect ?? defaultRect);
            }

            if (CurrentRow != null)
            {
                Rect? rect = GetRowRect(CurrentRow ?? 0);
                dc.DrawRectangle(CurrentBrush, CurrentPen, rect ?? defaultRect);
            }            

            if (HighlightedColumn != null && HighlightedColumn != 0)
            {
                Rect? rect = GetRect(0, HighlightedColumn ?? 0);
                dc.DrawRectangle(HighlightBrush, HighlightPen, rect ?? defaultRect);
            }

            if (HighlightedRow != null && HighlightedRow != 0)
            {

                Rect? rect = GetRect(HighlightedRow ?? 0, 0);
                dc.DrawRectangle(HighlightBrush, HighlightPen, rect ?? defaultRect);
            }

            base.OnRender(dc);           
        }

        void OnMouseMove(object sender, MouseEventArgs e)
        {
            Point pos = e.GetPosition(this);

            if (pos.X < 0 || pos.Y < 0 || pos.X > RenderSize.Width || pos.Y > RenderSize.Height)
            {
                Mouse.Capture(this, CaptureMode.None);
                return;
            }

            int? row = GetMouseRow(pos.Y);
            int? column = GetMouseColumn(pos.X);

            if (row != HighlightedRow || column != HighlightedColumn)
            {
                HighlightedRow = row;
                HighlightedColumn = column;                
            }            
        }

        protected override void OnMouseLeave(System.Windows.Input.MouseEventArgs e)
        {
            HighlightedRow = null;
            HighlightedColumn = null;            
            base.OnMouseLeave(e);
        }

        protected override void OnMouseEnter(MouseEventArgs e)
        {
            Mouse.Capture(this, CaptureMode.SubTree);
            base.OnMouseEnter(e);
        }

        protected virtual void OnHighlightChanged()
        {

        }
    }
}
